package com.bestcem.xm.member.handler.imports.easyexcel;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.exception.InvalidHeaderExcpetion;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.common.core.uitls.EnumUtil;
import com.bestcem.xm.member.constant.ExcelFieldConstants;
import com.bestcem.xm.member.entity.mongo.ExtendFieldValue;
import com.bestcem.xm.member.entity.pojo.ExtendFieldEnumItemDO;
import com.bestcem.xm.member.entity.pojo.MemberDO;
import com.bestcem.xm.member.enums.ExtendFieldTypeEnum;
import com.bestcem.xm.member.enums.ImportRecordStatusEnum;
import com.bestcem.xm.member.enums.MemberStatusEnum;
import com.bestcem.xm.member.service.ImportService;
import com.bestcem.xm.member.service.MemberService;
import com.bestcem.xm.member.service.dto.FieldDTO;
import com.bestcem.xm.member.service.param.ImportMemberParam;
import com.bestcem.xm.member.util.CheckUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

import static com.bestcem.xm.member.constant.Constants.*;
import static com.bestcem.xm.member.handler.imports.ImportExcelUtil.*;

/**
 * 解析内部联系人
 *
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/8/8
 */
@Slf4j
@Getter
@Setter
public class HttpReadInteriorMemberListener extends AnalysisEventListener<Map<Integer, String>> {

    /**
     * 表头数据
     */
    private List<String> headers;

    /**
     * 参数
     */
    private ImportMemberParam param;

    /**
     * MemberService
     */
    private MemberService memberService;

    /**
     * ImportService
     */
    private ImportService importService;

    /**
     * 更新进度的频率
     */
    private Integer per;

    /**
     * excel数据总数量
     */
    private Integer count;

    /**
     * 是否已经获取总数量
     */
    private boolean isGetCount;

    /**
     * 是否终止读取excel
     */
    private Boolean isStop;

    /**
     * 错误消息
     */
    private JSONArray errorMsgJsons;

    /**
     * 联系人数量
     */
    private Integer memberNumber;

    /**
     * 剩余的联系人数量
     */
    private Integer freeCount;

    private final static Map<String, Integer> EMPLOYEE_STATUS_MAP = new HashMap<String, Integer>() {{
        put("在职", 1);
        put("离职", 2);
    }};

    /**
     * excel 异常处理
     *
     * @param e
     * @param analysisContext
     * @throws Exception
     */
    @Override
    public void onException(Exception e, AnalysisContext analysisContext) throws Exception {
        if (log.isInfoEnabled()) {
            log.info("执行失败 {}", e.getMessage());
        }
        // 设置进度条结束
        // importService.deleteProgressBarStatus(0, param.getImportRecordId());
        // 继续抛出异常
        throw e;
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        // 获取表头数据
        if (log.isInfoEnabled()) {
            log.info("开始获取表头数据");
        }
        headers = new ArrayList<>();
        headMap.forEach((k, v) -> {
            if (StrUtil.isNotEmpty(v)) {
                headers.add(v);
            }
        });
        if (log.isInfoEnabled()) {
            log.info("开始校验表头数据");
        }
        // 校验表头
        Set<String> headerSet = new HashSet<>(headers);
        if (headers.size() != headerSet.size()) {
            // 列名不唯一
            throw new InvalidHeaderExcpetion("列名不唯一");
        }
    }


    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data
     * @param analysisContext
     */
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext analysisContext) {
        if (!isGetCount) {
            // 判断是否有表头数据，没有直接结束
            if (CollUtil.isEmpty(headers)) {
                throw new InvalidHeaderExcpetion("没有表头数据");
            }

            if (log.isInfoEnabled()) {
                log.info("开始获取总行数");
            }
            // 获取总行数
            Integer approximateTotalRowNumber =
                    analysisContext.readSheetHolder().getApproximateTotalRowNumber() - 1;
            count = approximateTotalRowNumber;
            if (log.isInfoEnabled()) {
                log.info("共有 {} 条数据", count);
            }
            if (approximateTotalRowNumber == 0) {
                // 没有数据 则终止读取excel
                isStop = true;
                return;
            }
            // 获取更新记录的频率
            per = importService.getCalcUpdateProgressStep(count);
            // 初始化进度
            importService.setProgressBarStatus(0, param.getImportRecordId(), 0, count);
            // 设置 读取总数量为true
            isGetCount = true;
        }
        // 获取当前解析的行数
        Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
        // fix 20211028 数据所在的行数
        Integer row = rowIndex + 1;
        // 判断是否为免费版
        ServiceResult<String> handleResult = handle(data, rowIndex);
        if (!handleResult.isSuccess()) {
            // 添加错误信息
            JSONObject messageJson = new JSONObject();
            messageJson.put(row.toString(), handleResult.getMsg());
            errorMsgJsons.add(messageJson);
        }

        if (rowIndex % per == 0) {
            // 每per条更新一次进度
            importService.setProgressBarStatus(0, param.getImportRecordId(), rowIndex, count);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 由于hasNext方法被重写,不一定能走到该方法
        if (!isStop) {
            finalOperation();
        }
    }

    /**
     * easyExcel 处理完所有数据之后,执行的后置操作
     *
     * @return void
     * @author zhangfeng <feng.zhang@idiaoyan.com>
     * @date 2021/8/31 15:42
     */
    private void finalOperation() {
        if (log.isInfoEnabled()) {
            log.info("所有数据解析完成");
        }

        //设置进度条结束
        importService.setProgressBarStatus(0, param.getImportRecordId(), count, count);
        int failedCount = errorMsgJsons.size();
        JSONObject dataJson = new JSONObject();
        dataJson.put("receiveCount", count);
        dataJson.put("successCount", count - failedCount);
        dataJson.put("failedCount", failedCount);
        dataJson.put("total_error_msg", errorMsgJsons);
        // 更新导入进度
        if (failedCount == count) {
            // 全部失败 设置记录为失败
            importService.updateImportRecordData(param.getImportRecordId(), dataJson, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_FAILED.getIndex());
        } else {
            // 部分失败
            importService.updateImportRecordData(param.getImportRecordId(), dataJson, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_SUCCESS.getIndex());
        }
    }

    /**
     * 处理数据
     *
     * @param data
     * @param rowIndex
     * @return
     */
    private ServiceResult<String> handle(Map<Integer, String> data, Integer rowIndex) {
        // 将excel 数据转换为json
        JSONObject memberJson = buildMemberJson(data, headers, INTER_FIELD_NAME_AND_FIELD_MAP);
        String errorMessage = "";
        // 校验数据
        errorMessage = checkErrorMsg(memberJson, rowIndex);
        if (StrUtil.isNotEmpty(errorMessage)) {
            // 若数据校验失败 则退出
            return ServiceResult.fail(errorMessage);
        }
        // 构建机构联系人数据
        MemberDO member = buildMember(memberJson, param);
        // 联系人扩展字段
        Map<String, ExtendFieldValue> fieldValList = new HashMap<>();
        Map<String, Boolean> updateFieldsIsNullMap = new HashMap<>();
        // 检查扩展字段与标签
        errorMessage = extendFieldLabel(memberJson, fieldValList, updateFieldsIsNullMap);
        if (StrUtil.isNotEmpty(errorMessage)) {
            return ServiceResult.fail(errorMessage);
        }
        // 保存联系人
        ServiceResult<Void> saveResult = save(member, fieldValList, updateFieldsIsNullMap);
        if (!saveResult.isSuccess()) {
            log.error(saveResult.getMsg());
            return ServiceResult.fail("未知错误");
        }
        return ServiceResult.success();
    }

    /**
     * 校验数据
     *
     * @param memberJson
     * @param rowIndex
     */
    private String checkErrorMsg(JSONObject memberJson, Integer rowIndex) {
        StringBuilder errMessage = new StringBuilder();
        // 检查主键列
        if (StrUtil.isEmpty(memberJson.getString(PRIMARY_KEY_MAP.get(param.getPrimaryKey())))) {
            return "主键不存在";
        }

        // 校验性别
        String gender = memberJson.getString(ExcelFieldConstants.INT_GENDER);
        if (StrUtil.isNotEmpty(gender) && !MEMBER_GENDER.contains(gender)) {
            errMessage.append("性别格式错误 ");
        }

        // 校验手机号
        String mobile = memberJson.getString(ExcelFieldConstants.INT_MOBILE);
        if (StrUtil.isNotEmpty(mobile)) {
            ServiceResult<String> result = CheckUtil.checkMobilePhoneFormat(mobile);
            if (!result.isSuccess()) {
                errMessage.append("手机号格式错误 ");
            }
        }

        // 校验邮箱
        String email = memberJson.getString(ExcelFieldConstants.INT_EMAIL);
        if (StrUtil.isNotEmpty(email)) {
            ServiceResult<String> result = CheckUtil.checkEmailFormat(email);
            if (!result.isSuccess()) {
                errMessage.append("邮箱格式错误 ");
            }
        }

        // 校验员工编号
        String employeeId = memberJson.getString(ExcelFieldConstants.INT_EMPLOYEE_ID);
        if (StrUtil.isNotEmpty(employeeId) && employeeId.length() > 50) {
            errMessage.append("员工编号长度超出限制 ");
        }

        // 校验生日
        String birthday = memberJson.getString(ExcelFieldConstants.INT_BIRTHDAY);
        if (StrUtil.isNotEmpty(birthday)) {
            ServiceResult<String> result = CheckUtil.checkDateTimeFormat(birthday, "yyyy/M/d");
            if (!result.isSuccess() || !checkDateValue(birthday)) {
                errMessage.append("生日格式错误 ");
            }
        }

        // 校验年龄
        Integer age = memberJson.getInteger(ExcelFieldConstants.INT_AGE);
        if (ObjectUtil.isNotEmpty(age)) {
            if (age < 0 || age > 200) {
                errMessage.append("出生日期超出限制 ");
            }
        }

        String name = memberJson.getString(ExcelFieldConstants.INT_NAME);
        // 姓名必填
        if (StrUtil.isEmpty(name)) {
            errMessage.append("姓名必填 ");
        }

        // 手机号码、邮箱、员工编号至少出现一个
        if (StrUtil.isEmpty(mobile) && StrUtil.isEmpty(email) && StrUtil.isEmpty(employeeId)) {
            errMessage.append("手机号码、邮箱、员工编号至少出现一个 ");
        }

        // 校验门店
        String store = memberJson.getString(ExcelFieldConstants.INT_STORE);
        Map<String, String> storeNameIdMap = param.getStoreNameIdMap();
        if (StrUtil.isNotEmpty(store) && CollUtil.isNotEmpty(storeNameIdMap) && !storeNameIdMap.containsKey(store)) {
            errMessage.append("所属组织不存在 ");
        }

        log.info(" 第 {} 行 {}", rowIndex, errMessage.toString());
        return errMessage.toString();
    }

    /**
     * 构建联系人数据
     *
     * @param memberJson
     * @param param
     * @return
     */
    private MemberDO buildMember(JSONObject memberJson, ImportMemberParam param) {
        // 获取与主键信息一致的联系人数据
        MemberDO member = memberService.queryMember(param.getOrgId(), param.getAttribute(), param.getPrimaryKey(), memberJson.getString(PRIMARY_KEY_MAP.get(param.getPrimaryKey())));
        // 获取联系人序号
        String memberNo = memberService.getMemberNo(param.getOrgId(), param.getAttribute());
        String birthdayStr = memberJson.getString(ExcelFieldConstants.INT_BIRTHDAY);
        JSONArray address = getAddress(memberJson);
        if (ObjectUtil.isNotEmpty(member)) {
            // 存在，则更新
            member.setOrgId(param.getOrgId());
            member.setSysId("member");
            member.setSource("excel");
            member.setName(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.INT_NAME), member.getName(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setEmail(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.INT_EMAIL), member.getEmail(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setPhone(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.INT_MOBILE), member.getPhone(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setGender(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.INT_GENDER), member.getGender(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setStoreId(dealUpdateFieldByString(param.getStoreNameIdMap().getOrDefault(memberJson.getString("store"), ""), member.getStoreId(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setAge(dealUpdateFieldByInteger(memberJson.getInteger(ExcelFieldConstants.INT_AGE), member.getAge(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setBirthday(dealUpdateFieldByDate(birthdayStr, member.getBirthday(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setAddress(dealUpdateFieldByString(Optional.ofNullable(address).map(String::valueOf).orElse(null), member.getAddress(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setMemberNo(dealUpdateFieldByString(memberNo, member.getMemberNo(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setEmployeeId(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.INT_EMPLOYEE_ID), member.getEmployeeId(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setEmployeeStatus(dealUpdateFieldByInteger(EMPLOYEE_STATUS_MAP.getOrDefault(memberJson.getString(ExcelFieldConstants.INT_EMPLOYEE_STATUS), null), member.getEmployeeStatus(), param.getExistedUpdate(), param.getNullUpdate()));
        } else {
            // 不存在 则插入
            member = new MemberDO();
            member.setOrgId(param.getOrgId());
            member.setSysId("member");
            member.setSource("excel");
            member.setName(memberJson.getString(ExcelFieldConstants.INT_NAME));
            member.setEmail(memberJson.getString(ExcelFieldConstants.INT_EMAIL));
            member.setPhone(memberJson.getString(ExcelFieldConstants.INT_MOBILE));
            member.setGender(memberJson.getString(ExcelFieldConstants.INT_GENDER));
            member.setStoreId(param.getStoreNameIdMap().getOrDefault(memberJson.getString(ExcelFieldConstants.INT_STORE), ""));
            member.setAge(memberJson.getInteger(ExcelFieldConstants.INT_AGE));
            if (StrUtil.isNotBlank(birthdayStr)) {
                member.setBirthday(DateUtil.convertStringToDate(birthdayStr, "yyyy/M/d"));
            } else {
                member.setBirthday(null);
            }
            member.setMemberNo(memberNo);
            member.setAddress(Optional.ofNullable(address).map(String::valueOf).orElse(null));
            member.setEmployeeId(memberJson.getString(ExcelFieldConstants.INT_EMPLOYEE_ID));
            member.setEmployeeStatus(EMPLOYEE_STATUS_MAP.getOrDefault(memberJson.getString(ExcelFieldConstants.INT_EMPLOYEE_STATUS), null));
        }
        member.setMemberType(param.getAttribute());
        // member.setNickname("");
        return member;
    }

    /**
     * 扩展字段 和 标签
     *
     * @param memberJson
     * @param fieldValList
     * @param updateFieldsIsNullMap
     */
    private String extendFieldLabel(JSONObject memberJson,
                                    Map<String, ExtendFieldValue> fieldValList,
                                    Map<String, Boolean> updateFieldsIsNullMap) {
        // 获取除基础字段之外的其中有字段（扩展字段与标签字段）
        StringBuilder stringBuilder = new StringBuilder("");
        Collection<String> extendHeaders = CollUtil.subtract(headers, INDIVIDUAL_BASE_EXCEL_HEADERS);
        if (CollUtil.isNotEmpty(extendHeaders)) {
            extendHeaders.forEach(extendHeader -> {
                Map<String, FieldDTO> fieldMap = param.getFiledMap();
                if (CollUtil.isNotEmpty(fieldMap) && fieldMap.containsKey(extendHeader)) {
                    // 获取该扩展字段对应的字段信息
                    FieldDTO fieldDTO = fieldMap.get(extendHeader);
                    // 扩展字段id
                    String id = fieldDTO.getId();
                    // 扩展字段类型
                    Integer type = fieldDTO.getType();
                    // 获取excel中该字段的值
                    String value = memberJson.getString(extendHeader);

                    if (StrUtil.isEmpty(id)) {
                        return;
                    }
                    if (StrUtil.isNotEmpty(value) && ObjectUtil.isNotEmpty(type)) {
                        // 如果 存在值 且扩展字段类型也存在 对数据进行校验
                        switch (EnumUtil.getEnumByIndex(ExtendFieldTypeEnum.class, type)) {
                            case ENUM:
                                Map<String, List<ExtendFieldEnumItemDO>> extendFieldEnumMap =
                                        param.getExtendFieldEnumMap();
                                if (CollUtil.isNotEmpty(extendFieldEnumMap)) {
                                    List<ExtendFieldEnumItemDO> extendFieldEnumItems = extendFieldEnumMap.get(id);
                                    if (CollUtil.isEmpty(extendFieldEnumItems)) {
                                        // 该枚举不存在选项
                                        stringBuilder.append(StrUtil.format("扩展字段:{} 枚举项未在枚举字段值内", extendHeader));
                                        return;
                                    }
                                    List<String> names = extendFieldEnumItems.stream()
                                            .map(ExtendFieldEnumItemDO::getName).collect(Collectors.toList());
                                    if (CollUtil.isEmpty(names)) {
                                        stringBuilder.append(StrUtil.format("扩展字段:{} 枚举项未在枚举字段值内", extendHeader));
                                        return;
                                    }
                                    if (!names.contains(value)) {
                                        stringBuilder.append(StrUtil.format("扩展字段:{} 枚举项未在枚举字段值内", extendHeader));
                                        return;
                                    }
                                } else {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 枚举项未在枚举字段值内", extendHeader));
                                    return;
                                }
                                break;
                            case DATETIME:
                                // 检查日期 excel中日期是以yyyy/MM/dd 格式定义的的 但读取出来是 yyyy-MM-dd HH:mm:ss
                                ServiceResult<String> dateResult = CheckUtil.checkDateTimeFormat(value, "yyyy-MM-dd HH:mm:ss");
                                if (!dateResult.isSuccess() || !checkDateValue(value)) {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 格式错误", extendHeader));
                                    return;
                                }
                                // 修改 日期格式 yyyy/MM/dd
                                value = cn.hutool.core.date.DateUtil.parse(value, "yyyy/M/d")
                                        .toString("yyyy/MM/dd");
                                break;
                            case NUMBER:
                                // 检查数字
                                ServiceResult<String> numberResult = CheckUtil.checkNumberFormat(value);
                                if (!numberResult.isSuccess() || !checkNumValue(value)) {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 格式错误", extendHeader));
                                    return;
                                }
                                break;
                            case MOBILE:
                                // 检查手机号
                                ServiceResult<String> mobileResult = CheckUtil.checkMobilePhoneFormat(value);
                                if (!mobileResult.isSuccess()) {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 手机号码格式错误", extendHeader));
                                    return;
                                }
                                break;
                            case EMAIL:
                                // 检查邮箱
                                ServiceResult<String> result = CheckUtil.checkEmailFormat(value);
                                if (!result.isSuccess()) {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 邮箱格式错误", extendHeader));
                                    return;
                                }
                                break;
                            default:
                        }
                    }
                    ExtendFieldValue extendFieldValue = new ExtendFieldValue();
                    extendFieldValue.setFieldType(type);
                    extendFieldValue.setFieldValue(value);
                    extendFieldValue.setFieldName(extendHeader);
                    extendFieldValue.setFieldId(id);
                    fieldValList.put(id, extendFieldValue);
                    updateFieldsIsNullMap.put(id, StrUtil.isEmpty(value));
                }
            });
        }
        return stringBuilder.toString();
    }

    /**
     * 保存 或更新联系人
     *
     * @param member
     * @param fieldValList
     * @param updateFieldsIsNullMap
     * @return
     */
    private ServiceResult<Void> save(MemberDO member,
                                     Map<String, ExtendFieldValue> fieldValList,
                                     Map<String, Boolean> updateFieldsIsNullMap) {
        if (StrUtil.isNotEmpty(member.getId())) {
            // 获取该联系人 原有的扩展字段信息
            String memberFieldValList = member.getFieldValList();
            if (StrUtil.isNotEmpty(memberFieldValList)) {
                // string 转 list
                List<ExtendFieldValue> rawFieldValList = JSONObject.parseArray(memberFieldValList, ExtendFieldValue.class);
                if (CollUtil.isNotEmpty(rawFieldValList)) {
                    rawFieldValList.forEach(field -> {
                        String fieldId = field.getFieldId();
                        if (fieldValList.containsKey(fieldId)) {
                            if (updateFieldsIsNullMap.getOrDefault(fieldId, false) && !param.getNullUpdate()) {
                                fieldValList.put(fieldId, field);
                            } else if (!param.getExistedUpdate()) {
                                fieldValList.put(fieldId, field);
                            }
                        } else {
                            fieldValList.put(fieldId, field);
                        }
                    });
                }
            }
            if (CollUtil.isNotEmpty(fieldValList)) {
                List<ExtendFieldValue> newFieldValList = new ArrayList<>();
                fieldValList.forEach((k, v) -> {
                    // ExtendFieldValue extendFieldValue = buildExtendValue(v);
                    newFieldValList.add(v);
                });
                member.setFieldValList(JSONObject.toJSONString(newFieldValList));
            }
            // 更新时间
            member.setUpdateTime(new Date());
            return memberService.updateImportMember(member);
        } else {
            // 创建
            if (CollUtil.isNotEmpty(fieldValList)) {
                List<ExtendFieldValue> newFieldValList = new ArrayList<>();
                fieldValList.forEach((k, v) -> {
                    // ExtendFieldValue extendFieldValue = buildExtendValue(v);
                    newFieldValList.add(v);
                });
                member.setFieldValList(JSONObject.toJSONString(newFieldValList));
            }
            // 创建时间
            member.setCreateTime(new Date());
            // 更新时间
            member.setUpdateTime(new Date());
            member.setStatus(MemberStatusEnum.ACTIVATED.getIndex());

            return memberService.createImportMember(member, param.getSaveMemberNum());
        }
    }

    /**
     * 重写hasNext 为了实现有问题直接退出
     *
     * @param context
     * @return
     */
    @Override
    public boolean hasNext(AnalysisContext context) {
        if (isStop) {
            // 如果非抛出异常终止,则后续进行的操作
            finalOperation();
        }
        return !isStop;
    }
}
